﻿$import( "SmitearWebGL.Core.PoolGroup" );

SmitearWebGL.Core.DataPool = Class.extend(
{
	/******** constructor ********/
	
	init : function( growSize )
	{
		/******** private property ********/
		
		this._groupList = [];
		this._totalMembers = 0;
		this._totalOpen = 0;
		
		this._groupCount = SmitearWebGL.Core.Math.nearestPowerOfTwo( growSize );
		this._indexShift = SmitearWebGL.Core.Math.lowestBitSet( this._groupCount );
		this._indexShift = SmitearWebGL.Core.Math.clamp( this._indexShift, 1, 15 );
		this._groupCount = 1 << this._indexShift;
		this._indexMask = this._groupCount - 1;
		
		this._registClass( "SmitearWebGL.Core.DataPool" );
	}
	,
	
	/******** public method ********/
	
	destroy : function()
	{
		for ( var i = 0; i < this._groupList.length; ++ i )
		{
			var group = this._groupList[i];
			group.destroy();
		}
	}
	,
	addMember : function( member )
	{
		var findGroupResult = this._findOpenGroup();
		var openGroup = findGroupResult.newGroup;
		var groupNumber = findGroupResult.groupNumber;

		var index = openGroup.addMember( member );

		-- this._totalOpen;

		return this._buildHandle( groupNumber, index );
	}
	,
	getMember : function( handle )
	{
		var group = this._getGroup( this._getGroupNumber( handle ) );
		var itemIndex = this._getDataIndex( handle );

		return group.member( itemIndex );
	}
	,
	removeMember : function( handle )
	{
		if ( this.isHandleValid( handle ) )
		{
			var groupIndex = this._getGroupNumber( handle );
			var dataIndex = this._getDataIndex( handle );
			var group = this._getGroup( groupIndex );
			group.removeRember( dataIndex );

			SmitearWebGL.Core.PoolGroup.CLEAR_HANDLE( handle );

			// 如果组内所有数据成员被释放, 则销毁组
			group = this._groupList[this._groupList.length - 1];
			if ( group.totalOpen() == this._groupCount )
			{
				group.destroy();
				this._groupList.pop();
			}

			++ this._totalOpen;
		}
	}
	,
	forEach : function( callBackFunction )
	{
		var groupNumber = 0;
		
		for ( var i = 0; i < this._groupList.length; ++ i )
		{
			var group = this._groupList[i];

			var callbackCount = group.totalUsed();
			var itemIndex = 0;
			while( callbackCount && itemIndex < this._groupCount )
			{
				// 如果组使用中
				if ( ! group.isOpen( itemIndex ) )
				{
					callBackFunction( this, this._buildHandle( groupNumber, itemIndex ), group.member( itemIndex ) );
					-- callbackCount;
				}
				++ itemIndex;
			}
			++ groupNumber;
		}
	}
	,
	isHandleValid : function( handle )
	{
		// 如果句柄有效
		if ( SmitearWebGL.Core.PoolGroup.VALID_HANDLE( handle ) )
		{
			var group = this._getGroup( this._getGroupNumber( handle ) );

			// 如果数据成员不在使用中
			return( ! group.isOpen( this._getDataIndex( handle ) ) );

		}
		return false;
	}
	,
	totalMembers : function()
	{
		return this._totalMembers;
	}
	,
	totalOpen : function()
	{
		return this._totalOpen;
	}
	,
	totalUsed : function()
	{
		return this._totalMembers - this._totalOpen;
	}
	,
	
	/******** private method ********/
	
	_findOpenGroup : function()
	{
		// 找到并返回拥有空槽的第一个组
		var groupNumber = 0;
		
		for ( var i = 0; i < this._groupList.length; ++ i )
		{
			var group = this._groupList[i];
			if ( group.totalOpen() )
			{
				return { newGroup : group, groupNumber : groupNumber };
			}
			++ groupNumber;
		}

		// 如果没有打开的槽, 则创建一个新组, 但必须确保没有超过MAX_UINT16个成员
		$THROW( this._groupList.length * ( this._groupCount + 1 ) < SmitearWebGL.Core.Math.MAX_INT_VALUE, "资源池已经满载!!!!" );

		return { newGroup : this._addGroup(), groupNumber : groupNumber };
	}
	,
	_addGroup : function()
	{
		var newGroup = new SmitearWebGL.Core.PoolGroup( this._groupCount );
		
		this._groupList.push( newGroup );
		this._totalMembers += this._groupCount;
		this._totalOpen += this._groupCount;

		return newGroup;
	}
	,
	_getGroup : function( index )
	{
		$THROW( index < this._groupList.length, "组索引无效" );

		return this._groupList[index];
	}
	,
	_getGroupNumber : function( handle )
	{
		return handle >> this._indexShift;
	}
	,
	_getDataIndex : function( handle )
	{
		return handle & this._indexMask;
	}
	,
	_buildHandle : function( group, index )
	{
		return ( group << this._indexShift ) + index;
	}
}
);